/**
*  \file tlf.c
*
*  \brief Tlf35584: multi voltage safety micro processor supply
*
*  \author Qilong Huang
*
*
*/
/*===============================================[ private includes  ]================================================*/

#include "xmc_gpio.h"
#include "../pal/pal_types.h"
#include "../xmc4500/types_local.h"
#include "../xmc4500/spi_hw.h"
#include "../mcu/mcu.h"
#include "../timerecorder/timerecorder.h"
#include "../output/output.h"
#include "tlf.h"
#include <xmc_gpio.h>
#include <xmc_spi.h>
/*===============================================[ private defines   ]================================================*/


#define tlf_read                  0
#define tlf_write                 BIT14
void spi_delay(U16 nnops);
void tlf_switch_state(TLF_DEVCTL_t *status);
BO parit_cal(U16 data_in);
static int tlf_read_cnt = 0;
U16 tlf_op_reg(tlf_op tlf_read_or_write, tlf_reg_addr tlf_reg, u8 op_data);
/*===============================================[ private datatypes ]================================================*/
/*in manula P163*/




/*===============================================[ private variables ]================================================*/


/*===============================================[ public variables  ]================================================*/
u8 TLF_Reg_sysfail;
u8 TLF_Reg_intterr;
u8 TLF_Reg_if;
u8 TLF_Reg_syssf;
u8 TLF_Reg_otfail;
u8 TLF_Reg_otwrnsf;
u8 TLF_Reg_monsf0;
u8 TLF_Reg_monsf1;
u8 TLF_Reg_monsf2;
u8 TLF_Reg_monsf3;
u8 TLF_Reg_spisf;
u8 TLF_Reg_wksf;
/*================================================[ inline functions ]================================================*/





/*===============================================[ private functions ]================================================*/
U16 tlf_read_write_reg(U16 data);

/*================================================[ public functions ]================================================*/


/**
**
* @brief    This function is to init the TLF35584 PMIC, It mainly do 4 things:
            1. Unlock the protected register;
            2. Set the WTDG register to disable it;
            3. Set the errpin register to disable it;
            4. Lock the protected register.
* @param    none
* @note     This function is called in file 'hardware.c'
            It should be called as soon when the spi is initialized.
* @retval   void
**
*/
void tlf_init(void)
{
    TLF_DEVCTL_t tlf_devctl_def;
    mcu_set_output(mcu_output_TLF_MPS_CON, 0x01);
    /*Unlock the protected registers*/
    tlf_read_write_reg(tlf_WR_UNLOCK_KEY1);
    tlf_read_write_reg(tlf_WR_UNLOCK_KEY2);
    tlf_read_write_reg(tlf_WR_UNLOCK_KEY3);
    tlf_read_write_reg(tlf_WR_UNLOCK_KEY4);

    tlf_read_write_reg(tlf_WWDG_CYCLE1600);         //Set WWDG 1600 cycles
    tlf_read_write_reg(tlf_DIS_WWDG);               //Disable the WWDG
    tlf_read_write_reg(tlf_DIS_ERR_PIN);            //Disable the Err pin
    //tlf_op_reg(tlf_write, tlf_syscfg1, 0x08);
    /*Lock the protected registers*/
    tlf_read_write_reg(tlf_WR_LOCK_KEY1);
    tlf_read_write_reg(tlf_WR_LOCK_KEY2);
    tlf_read_write_reg(tlf_WR_LOCK_KEY3);
    tlf_read_write_reg(tlf_WR_LOCK_KEY4);
    mcu_set_output(mcu_output_TLF_MPS_CON, 0x01);
    //tlf_devctl_def.pmic_status = TLF_NORMAL;
    //tlf_devctl_def.request_pwr_status = tlf_trk2en | tlf_trk1en | tlf_comen | tlf_vrefen;
    //tlf_switch_state(&tlf_devctl_def);
    //tlf_op_reg(tlf_write, tlf_devctrl, 0xEA);
    //tlf_op_reg(tlf_write, tlf_devctrln, 0x15);
    tlf_read_write_reg(0xABD5);
    tlf_read_write_reg(0xAC2B);
}/*End of tlf_init()*/


void tlf_service_10ms(void)
{
    U16 data_rec;
    tlf_read_cnt ++;
    /*Note: The PMIC will not enter normal mode until the power is stable. So It should be check status, if the IC is not enter into normal mode, do it twice.*/
    if((tlf_op_reg(tlf_read, tlf_devstat, 0) >> 1) != 0xFA)
    {
        tlf_read_write_reg(0xABD5);
        tlf_read_write_reg(0xAC2B);
    }

    #if DEBUG_TEST_TLF35584
	  
	//output_data.v_debug_2[1] = tlf_read_cnt;
    switch(tlf_read_cnt)
    {
        case SYSFAIL_READ_CYCLE:
        {
            TLF_Reg_sysfail = (u8)((tlf_read_write_reg(tlf_MON_SYSFAIL) >> 1) & 0x00ff);
            //output_data.v_debug_2[2] = (output_data.v_debug_2[2] & 0x00ff) | (data_rec << 8);
            break;
        }
        case INITERR_READ_CYCLE:
        {
            TLF_Reg_intterr = (u8)((tlf_read_write_reg(tlf_MON_INITERR) >> 1) & 0x00ff);
            //output_data.v_debug_2[2] = (output_data.v_debug_2[2] & 0xff00) | data_rec;
            break;
        }
        case IF_READ_CYCLE:
        {
            TLF_Reg_if = (u8)((tlf_read_write_reg(tlf_MON_IF) >> 1) & 0x00ff);
            //output_data.v_debug_2[3] = (output_data.v_debug_2[3] & 0x00ff) | (data_rec << 8);
            break;
        }
        case SYSSF_READ_CYCLE:
        {
            TLF_Reg_syssf = (u8)((tlf_read_write_reg(tlf_MON_SYSSF) >> 1) & 0x00ff);
            //output_data.v_debug_2[3] = (output_data.v_debug_2[3] & 0xff00) | data_rec;
            break;
        }
        case OTFAIL_READ_CYCLE:
        {
            TLF_Reg_otfail = (u8)((tlf_read_write_reg(tlf_MON_OTFAIL) >> 1) & 0x00ff);
            //output_data.v_debug_3[0] = (output_data.v_debug_3[0] & 0x00ff) | (data_rec << 8);
            break;
        }
        case OTWRNSF_READ_CYCLE:
        {
            TLF_Reg_otwrnsf = (u8)((tlf_read_write_reg(tlf_MON_OTWRNSF) >> 1) & 0x00ff);
            //output_data.v_debug_3[0] = (output_data.v_debug_3[0] & 0xff00) | data_rec;
            break;
        }
        case MONSF0_READ_CYCLE:
        {
            TLF_Reg_monsf0 = (u8)((tlf_read_write_reg(tlf_MON_MONSF0) >> 1) & 0x00ff);
            //output_data.v_debug_3[1] = (output_data.v_debug_3[1] & 0x00ff) | (data_rec << 8);
            break;
        }
        case MONSF1_READ_CYCLE:
        {
            TLF_Reg_monsf1 = (u8)((tlf_read_write_reg(tlf_MON_MONSF1) >> 1) & 0x00ff);
            //output_data.v_debug_3[1] = (output_data.v_debug_3[1] & 0xff00) | data_rec;
            break;
        }
        case MONSF2_READ_CYCLE:
        {
            TLF_Reg_monsf2 = (u8)((tlf_read_write_reg(tlf_MON_MONSF2) >> 1) & 0x00ff);
            //output_data.v_debug_3[2] = (output_data.v_debug_3[2] & 0x00ff) | (data_rec << 8);
            break;
        }
        case MONSF3_READ_CYCLE:
        {
            TLF_Reg_monsf3 = (u8)((tlf_read_write_reg(tlf_MON_MONSF3) >> 1) & 0x00ff);
            //output_data.v_debug_3[2] = (output_data.v_debug_3[2] & 0xff00) | data_rec;
            break;
        }
        case SPISF_READ_CYCLE:
        {
            TLF_Reg_spisf = (u8)((tlf_read_write_reg(tlf_MON_SPISF) >> 1) & 0x00ff);
            //output_data.v_debug_3[3] = (output_data.v_debug_3[3] & 0x00ff) | (data_rec << 8);
            break;
        }
        case WKSF_READ_CYCLE:
        {
            //data_rec = ((tlf_read_write_reg(tlf_DEVSTAT) >> 1) & 0x00ff);
            //data_rec = ((tlf_op_reg(tlf_MON_WKSF) >> 1) & 0x00ff);
            //output_data.v_debug_3[3] = (output_data.v_debug_3[3] & 0xff00) | data_rec; 
            //data_rec = ((tlf_op_reg(tlf_read, tlf_devstat, 0) >> 1) & 0x00ff);
            TLF_Reg_wksf = (u8)((tlf_op_reg(tlf_read, tlf_MON_WKSF, 0) >> 1) & 0x00ff);
            //output_data.v_debug_3[3] = (output_data.v_debug_3[3] & 0xff00) | data_rec;
            break;
        }
        case RWWDCFG0_READ_CYCLE_READ_CYCLE:
        {
            //data_rec = ((tlf_read_write_reg(tlf_RWWDCFG0) >> 1) & 0x00ff);
            //output_data.v_debug_4[2] = (output_data.v_debug_4[2] & 0xff00) | data_rec; 
            break;
        }
        case RWDCFG0_READ_CYCLE_READ_CYCLE:
        {
            //data_rec = ((tlf_read_write_reg(tlf_RWDCFG0) >> 1) & 0x00ff);
            //output_data.v_debug_4[3] = (output_data.v_debug_4[3] & 0x00ff) | (data_rec << 8); 
            break;
        }
        case PROTSTAT_READ_CYCLE_READ_CYCLE:
        {
            tlf_read_cnt = 0;
            //data_rec = ((tlf_read_write_reg(tlf_PROTSTAT) >> 1) & 0x00ff);
            //output_data.v_debug_4[3] = (output_data.v_debug_4[3] & 0xff00) | data_rec; 
            break;
        }

        default: break;
    } 
    #endif
}

/**
**
* @brief    This function is to read and write TLF35584 PMIC:
* @param    none
* @note     This function is define in file 'spi_hw.c'
* @retval   unsigned short int(16 Bit)
**
*/
U16 tlf_read_write_reg(U16 data)
{
    return write_spi_word(data);
}/*End of tlf_read_write_reg()*/
/*===============================================[ private functions ]================================================*/


/*write SYSPCFG0, SYSPCFG1, WDCFG0, WDCFG1, FWDCFG, WWDCFG0, WWDCFG1 need to unlock*/
void tlf_config_unlock(void)
{   
    tlf_read_write_reg(0x8756);
    tlf_read_write_reg(0x87DE);
    tlf_read_write_reg(0x86AD);
    tlf_read_write_reg(0x8625);
}
void tlf_config_lock(void)
{
    tlf_read_write_reg(0x87BE);
    tlf_read_write_reg(0x8668);
    tlf_read_write_reg(0x877D);
    tlf_read_write_reg(0x8795);
}

/**
**
* @brief    This function is to operate the PMIC TLF35584.
 *          When using this function, you should:
 *          1. Appoint the operation, read the register or read the register;
 *          2. The register address you want to operate;
 *          3. If you want to write someting to the register, you should transmit a 1-byte data to this function.
 *          When these parameters correctly transmitted, this function will calculate the right spi cmd and then communicate with the PMIC
 * @param   1. tlf_op tlf_read_or_write : read data from the PMIC or write data to the PMIC.
 *                  tlf_read: read date from TLF, tlf_write: write data to TLF
 *          2. tlf_reg_addr tlf_reg     : the register address you want to operate, all registers list were define in the enum tlf_reg_addr;
 *          3. u8 op_data               : one byte data, if you want to write data to TLF, this parameter is necessery. 
 *                                        when you just read data from TLF, it should be set to '0x00' as default
 * @retval  U16(unsigned short int)         : Each time when the data was set to PMIC successfully, a return value will be display.
 * @note    Note that, the return value is transmitted out directly, when you receive this value, you should check the value.
 *          return value format when read:
 *          |=============================================================================================|
 *          |   Bit[15]   |         Bit[14:9]         |           Bit[8:1]           |      Bit[0]        |
 *          |---------------------------------------------------------------------------------------------|
 *          |     CMD     |        Status[4:0]        |           Data[7:0]          |      Parity        | 
 *          |---------------------------------------------------------------------------------------------|
 *          |      1      |         0b000000          |          0bxxxxxxxx          |    PF (1 or 0)     | 
 *          |=============================================================================================|
 *          return value format when write:
 *          |=============================================================================================|
 *          |   Bit[15]   |         Bit[14:9]         |           Bit[8:1]           |      Bit[0]        |
 *          |---------------------------------------------------------------------------------------------|
 *          |     CMD     |    write address[4:0]     |       write data[7:0]        |      Parity        | 
 *          |---------------------------------------------------------------------------------------------|
 *          |      1      |          0bxxxxxx         |          0bxxxxxxxx          |    PF (1 or 0)     | 
 *          |=============================================================================================|
**      
*/
U16 tlf_op_reg(tlf_op tlf_read_or_write, tlf_reg_addr tlf_reg, u8 op_data)
{
    TLF_REGDATA_t reg_data;
    BO data_parity;
    U16 pd_ret;
    reg_data.reg_addr = tlf_reg;
    reg_data.op = tlf_read_or_write;
    if(reg_data.op == tlf_read)
    {
        reg_data.cmd_data = 0x00;
    }
    else
    {
        reg_data.cmd_data = op_data;
    }
    reg_data.parity = 0;
    /*caculate the parite of the data*/
    data_parity = parit_cal(reg_data.data_to_send);
    if(data_parity != 0)
    {
        reg_data.parity = 1;
    }
    else
    {
        reg_data.parity = 0;
    }
    pd_ret = tlf_read_write_reg(reg_data.data_to_send);
    return pd_ret;
}/*End of tlf_op_reg()*/
/**
**
* @brief    This function is to switch the TLF35584 PMIC work status.
 * TLF35584 status commond, when set the status, 2 registers should be set at the same time
 *      1. tlf_DEVCTRL_NORMAL_STATUS  : DEVCTRL commod for normal status
 *      2. tlf_DEVCTRLN_NORMAL_STATUS : DEVCTRLN commod for normal status
 *             1. Set the DEVCTRL(Device control request, Address:15H)
            ================================================
               7   |    6   |   5   |  4 |   3    |  2 1 0
            ------------------------------------------------            
            TRK2EN | TRK1EN | COMEN | nu | VREFEN | STATEREQ
            ================================================
            2. Invert the data in the DEVCTRL register, and write it into DEVCTRLN(Device control inverted request,Address:16H)
            ================================================
               7   |    6   |   5   |  4 |   3    |  2 1 0
            ------------------------------------------------            
            TRK2EN | TRK1EN | COMEN | nu | VREFEN | STATEREQ
            ================================================
*
* @param    none
* @note 
* @retval   void
**
*/
void tlf_switch_state(TLF_DEVCTL_t *status)
{
    U16 devctrl_data, devctrln_data;
    U16 devctrl_address = tlf_devctrl, devctrln_address = tlf_devctrln;
    BO data_parity;
    U16 devctrl_val = (U16)status->devctrl;
    devctrl_data = (devctrl_address << 9) | (devctrl_val << 1) | 0x8000;
    data_parity = parit_cal(devctrl_data);
    if(data_parity != 0)
    {
        devctrl_data |= 0x00001;
    }
    else
    {
        devctrl_data &= ~0x00001;
    }
    devctrln_data = (devctrln_address << 9) | ((~((devctrl_val) << 1)) & 0x01FE) | 0x8000;
    data_parity = parit_cal(devctrln_data);
    if(data_parity != 0)
    {
        devctrln_data |= 0x00001;
    }
    else
    { 
        devctrln_data &= ~0x00001;
    }
    tlf_read_write_reg(devctrl_data);      //write to REG:DEVCTRL
    tlf_read_write_reg(devctrln_data);     //write to REG:DEVCTRLN
}/*End of tlf_switch_state()*/


/**
**
* @brief    This function is to calcualte the parity of a word.
* @param    U16 data_in: The data you want to calculate.
* @note 
* @retval   BOOL, 0 is EVEN, !0 is ODD
**
*/
BO parit_cal(U16 data_in)
{
    U16 data_temp = data_in;
    BO parity = FALSE;
    while(data_temp)
    {
        parity = !parity;
        data_temp = data_temp & (data_temp - 1);
    }
    return parity;
}/*End of parit_cal()*/

void spi_delay(U16 nnops)
{
    U16 i, j;
    for(i = 0; i < 5; i ++)
    {
        for(j = 0; j < nnops; j ++)
        {
            ;
        }
    }
}
/***********************************************************************************************************************
*                             Copyright 2016 Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/

